package com.atgm.mall.gmmailorder.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atgm.common.constant.RabbitMqConstant;
import com.atgm.common.exception.BizCodeEnume;
import com.atgm.common.exception.OrderException;
import com.atgm.common.utils.GmMallUtils;
import com.atgm.common.utils.R;
import com.atgm.common.vo.MemberResponseVo;
import com.atgm.mall.gmmailorder.constant.OrderConstant;
import com.atgm.mall.gmmailorder.constant.OrderStatusEnum;
import com.atgm.mall.gmmailorder.dao.OrderItemDao;
import com.atgm.mall.gmmailorder.entity.OrderItemEntity;
import com.atgm.mall.gmmailorder.feign.CartFeignService;
import com.atgm.mall.gmmailorder.feign.MemberFeignService;
import com.atgm.mall.gmmailorder.feign.ProductFeignService;
import com.atgm.mall.gmmailorder.feign.WmsFeignService;
import com.atgm.mall.gmmailorder.feign.vo.CartRemoveVo;
import com.atgm.mall.gmmailorder.feign.vo.OrderGameVo;
import com.atgm.mall.gmmailorder.feign.vo.WareGameItemVo;
import com.atgm.mall.gmmailorder.feign.vo.WareGameReqVo;
import com.atgm.mall.gmmailorder.interceptor.LoginUserInterceptor;
import com.atgm.mall.gmmailorder.service.OrderItemService;
import com.atgm.mall.gmmailorder.vo.*;
import com.atgm.mall.gmmailorder.vo.req.OrderReqVo;
import com.atgm.common.to.PayOrderVo;
import com.atgm.mall.gmmailorder.vo.resp.OrderDetailRespVo;
import com.atgm.mall.gmmailorder.vo.resp.OrderItemRespVo;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atgm.common.utils.PageUtils;
import com.atgm.common.utils.Query;

import com.atgm.mall.gmmailorder.dao.OrderDao;
import com.atgm.mall.gmmailorder.entity.OrderEntity;
import com.atgm.mall.gmmailorder.service.OrderService;

@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    private ThreadLocal<OrderSubmitVo> confirmVoThreadLocal = new ThreadLocal<>();

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    WmsFeignService wmsFeignService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private OrderItemDao orderItemDao;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    ThreadPoolExecutor executor;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderItemRespVo handlerOrerConfirm(List<Long> gameIds) {
        OrderItemRespVo orderItemRespVo = new OrderItemRespVo();
        //获取商品信息
        R r = productFeignService.getOrderGameInfo(gameIds);
        List<OrderGameVo> orderGameVos = r.getData(new TypeReference<List<OrderGameVo>>() {});
        BigDecimal totalPrice = new BigDecimal(0);  //商品总价
        BigDecimal favorablePrice = new BigDecimal(0);  //活动优惠
        BigDecimal payablePrice = new BigDecimal(0);  //应付价格
        if (GmMallUtils.isNotEmpty(orderGameVos)) {
            for (OrderGameVo orderGameVo : orderGameVos) {
                totalPrice = totalPrice.add(orderGameVo.getPrice());
                payablePrice = payablePrice.add(orderGameVo.getSeckillPrice() != null ? orderGameVo.getSeckillPrice() : orderGameVo.getPrice());
                if (orderGameVo.getSeckillPrice() != null) {
                    favorablePrice = favorablePrice.add(orderGameVo.getPrice().subtract(orderGameVo.getSeckillPrice()));
                }
            }
        }
        orderItemRespVo.setOrderGameVos(orderGameVos);
        orderItemRespVo.setTotalPrice(totalPrice);
        orderItemRespVo.setFavorablePrice(favorablePrice);
        orderItemRespVo.setPayablePrice(payablePrice);

        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
        Long memberId = memberResponseVo.getId();

        String orderToken = UUID.randomUUID().toString().replace("_", "");

        redisTemplate.opsForValue().set(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberId.toString(), orderToken, 30, TimeUnit.MINUTES);
        orderItemRespVo.setOrderToken(orderToken);
        //设置原子令牌
        return orderItemRespVo;
    }

    @Override
    public List<OrderDetailRespVo> queryOrderList(Integer status) {
        List<OrderDetailRespVo> outList = new ArrayList<>();
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
        Long memberId = memberResponseVo.getId();  //用户id

        QueryWrapper<OrderEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("member_id", memberId);
        if (null != status) {
            queryWrapper.eq("status", status);
        }
        queryWrapper.orderByDesc("create_time");
        List<OrderEntity> orderEntities = baseMapper.selectList(queryWrapper);
        if (GmMallUtils.isEmpty(orderEntities)) {
            return null;
        }
        orderEntities.forEach(orderEntity -> {
            OrderDetailRespVo orderDetailRespVo = new OrderDetailRespVo();
            orderDetailRespVo.setOrderSn(orderEntity.getOrderSn());
            orderDetailRespVo.setCreateTime(orderEntity.getCreateTime());
            orderDetailRespVo.setPrice(orderEntity.getPayAmount());
            orderDetailRespVo.setStatus(orderEntity.getStatus());
            orderDetailRespVo.setStatusName(OrderConstant.orderStatusMap.get(orderEntity.getStatus()));
            outList.add(orderDetailRespVo);
        });

        //获取订单项信息
        List<String> orderSnList = orderEntities.stream().map(OrderEntity::getOrderSn).collect(Collectors.toList());
        QueryWrapper<OrderItemEntity> orderItemEntityQueryWrapper = new QueryWrapper<>();
        orderItemEntityQueryWrapper.in("order_sn", orderSnList);
        List<OrderItemEntity> orderItemEntities = orderItemDao.selectList(orderItemEntityQueryWrapper);
        outList.forEach(orderDetailRespVo -> {
            List<OrderItemEntity> list = orderItemEntities.stream().filter(orderItemEntity -> {
                return orderItemEntity.getOrderSn().equals(orderDetailRespVo.getOrderSn());
            }).collect(Collectors.toList());
            orderDetailRespVo.setOrderItems(list);
        });
        return outList;
    }

    @Override
    public OrderDetailRespVo queryOrderInfo(String orderSn) {
        OrderDetailRespVo orderDetailRespVo = new OrderDetailRespVo();
        QueryWrapper<OrderEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_sn", orderSn);
        OrderEntity orderEntity = baseMapper.selectOne(queryWrapper);
        if (null == orderEntity) {
            throw new OrderException("该订单号[" + orderSn + "]不存在", BizCodeEnume.SUBMIT_ORDER_FAIL.getCode());
        }

        orderDetailRespVo.setOrderSn(orderSn);
        orderDetailRespVo.setStatus(orderEntity.getStatus());
        orderDetailRespVo.setCreateTime(orderEntity.getCreateTime());
        orderDetailRespVo.setPrice(orderEntity.getPayAmount());
        orderDetailRespVo.setStatusName(OrderConstant.orderStatusMap.get(orderEntity.getStatus()));

        QueryWrapper<OrderItemEntity> orderItemEntityQueryWrapper = new QueryWrapper<>();
        orderItemEntityQueryWrapper.eq("order_sn", orderSn);
        List<OrderItemEntity> orderItemEntities = orderItemDao.selectList(orderItemEntityQueryWrapper);
        if (GmMallUtils.isNotEmpty(orderItemEntities)) {
            List<OrderItemEntity> list = orderItemEntities.stream().filter(orderItemEntity -> {
                return orderItemEntity.getOrderSn().equals(orderSn);
            }).collect(Collectors.toList());
            orderDetailRespVo.setOrderItems(list);
        }

        return orderDetailRespVo;
    }

    @Override
    public BigDecimal queryPriceByOrderSn(String orderSn) {
        QueryWrapper<OrderEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("pay_amount");
        queryWrapper.eq("order_sn", orderSn);
        OrderEntity orderEntity = baseMapper.selectOne(queryWrapper);
        return orderEntity == null ? null : orderEntity.getPayAmount();
    }

    @Override
    public void updateOrderPay(PayOrderVo payOrderVo) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setStatus(payOrderVo.getStatus());
        orderEntity.setPayType(payOrderVo.getPayType());
        orderEntity.setSourceType(payOrderVo.getSourceType());
        orderEntity.setPaymentTime(payOrderVo.getPaymentTime());

        QueryWrapper<OrderEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_sn", payOrderVo.getOrderSn());
        int count = baseMapper.update(orderEntity, queryWrapper);
    }

    @Override
    @GlobalTransactional
    public void updateStatusInfo(String orderSn, Integer status) {
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setStatus(status);
        QueryWrapper<OrderEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("order_sn", orderSn);
        int count = baseMapper.update(orderEntity, queryWrapper);

        //取消订单，释放库存
        if (4 == status) {
            wmsFeignService.unlockOrder(orderSn);
        }
    }

    /**
     * 事务的隔离级别、传播，
     * 本地事务调用失败：本类事务方法调用本类中的事务方法，this.xxx()  不会生效，  因为事务使用代理实现，
     * 解决：引用starter-aop，@EnableAspectJAutoProxy 开启动态代理
     * 分布式事务
     */
    @Override
    @GlobalTransactional
    public String submitOrderInfo(OrderReqVo orderReqVo) {
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
        Long memberId = memberResponseVo.getId();
        //验证令牌是否合法 保证操作的原子性
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        //原子令牌
        String orderToken = orderReqVo.getOrderToken();
        Long execute = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Arrays.asList(OrderConstant.USER_ORDER_TOKEN_PREFIX + memberId.toString()), orderToken);

        log.info("execute : {}", execute);
        if (1 == execute) {
            //1、创建订单
            OrderDetail orderInfo = createOrderInfo(orderReqVo);

            //2、保存订单、订单项
            int count = baseMapper.insert(orderInfo.getOrderEntity());
            if (count != 1) {
                throw new OrderException(BizCodeEnume.SUBMIT_ORDER_FAIL.getMsg(), BizCodeEnume.SUBMIT_ORDER_FAIL.getCode());
            }

            boolean saveBatch = orderItemService.saveBatch(orderInfo.getOrderItemEntities());
            if (!saveBatch) {
                throw new OrderException(BizCodeEnume.SUBMIT_ORDER_FAIL.getMsg(), BizCodeEnume.SUBMIT_ORDER_FAIL.getCode());
            }

            //锁定库存内容
            WareGameReqVo wareGameReqVo = new WareGameReqVo();
            List<WareGameItemVo> wareGameItemVos = new ArrayList<>();
            orderInfo.getOrderItemEntities().forEach(orderItemEntity -> {
                WareGameItemVo wareGameItemVo = new WareGameItemVo();
                wareGameItemVo.setGameId(orderItemEntity.getGameId());
                wareGameItemVo.setGameName(orderItemEntity.getGameName());
                wareGameItemVo.setStock(orderItemEntity.getGameQuantity());
                wareGameItemVos.add(wareGameItemVo);
            });
            wareGameReqVo.setOrderSn(orderInfo.getOrderEntity().getOrderSn());
            wareGameReqVo.setList(wareGameItemVos);
            wmsFeignService.lockOrder(wareGameReqVo);

            //删除购物车中数据
            CartRemoveVo cartRemoveVo = new CartRemoveVo();
            cartRemoveVo.setMemberId(memberId);
            List<Long> gameIds = orderInfo.getOrderItemEntities().stream().map(OrderItemEntity::getGameId).collect(Collectors.toList());
            cartRemoveVo.setGameIds(gameIds);
            cartFeignService.removeCart(cartRemoveVo);

            //发送rabbitmq通知，创建订单，放入死信队列，15分钟后，没有支付，关闭订单
            rabbitTemplate.convertAndSend(RabbitMqConstant.Exchange.ORDER_EVENT_EXCHANGE, RabbitMqConstant.Queue.ORDER_CREATE_ROUTING_LEY, orderInfo.getOrderEntity().getOrderSn());

            return orderInfo.getOrderEntity().getOrderSn();
        }else {
            log.error("原子令牌失效");
            throw new OrderException(BizCodeEnume.SUBMIT_ORDER_FAIL.getMsg(), BizCodeEnume.SUBMIT_ORDER_FAIL.getCode());
        }
    }

    /**
     * 创建订单
     */
    private OrderDetail createOrderInfo(OrderReqVo orderReqVo) {
        OrderDetail orderDetail = new OrderDetail();
        //1、创建订单号
        String orderSn = IdWorker.getTimeId();

        //2、生成订单信息
        OrderEntity orderEntity = builderOrder(orderSn, orderReqVo);
        orderDetail.setOrderEntity(orderEntity);
        //3、生成订单项数据
        List<OrderItemEntity> orderItemEntities = builderOrderItem(orderSn, orderReqVo.getGameIds());
        orderDetail.setOrderItemEntities(orderItemEntities);

        //4、校验交个信息
        checkTotalPrice(orderEntity, orderItemEntities);

        log.info("订单信息 ：{}", orderDetail);
        return orderDetail;
    }

    /**
     * 生成订单信息
     * @param orderSn
     * @param orderReqVo
     */
    private OrderEntity builderOrder(String orderSn, OrderReqVo orderReqVo) {
        MemberResponseVo memberResponseVo = LoginUserInterceptor.loginUser.get();
        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setOrderSn(orderSn);
        orderEntity.setCreateTime(new Date());
        orderEntity.setMemberId(memberResponseVo.getId());
        orderEntity.setMemberUsername(memberResponseVo.getUsername());
        orderEntity.setPayAmount(orderReqVo.getPayablePrice()); //应付金额
        orderEntity.setStatus(OrderStatusEnum.CREATE_NEW.getCode());
        orderEntity.setConfirmStatus(0);  //未确认收货
        orderEntity.setAutoConfirmDay(1);  //自动确认时间
        return orderEntity;
    }

    /**
     * 构建订单项
     * @param orderSn
     * @param gameIds
     */
    private List<OrderItemEntity> builderOrderItem(String orderSn, List<Long> gameIds) {
        List<OrderItemEntity> orderItemEntities = new ArrayList<>();
        //获取游戏商品信息
        R r = productFeignService.getOrderGameInfo(gameIds);
        List<OrderGameVo> gameVoList = r.getData(new TypeReference<List<OrderGameVo>>() {});
        gameVoList.forEach(orderGameVo -> {
            OrderItemEntity orderItemEntity = new OrderItemEntity();
            orderItemEntity.setOrderSn(orderSn);
            orderItemEntity.setGameId(orderGameVo.getGameId());
            orderItemEntity.setGameName(orderGameVo.getGameName());
            orderItemEntity.setGameDefImg(orderGameVo.getDefaultImg());
            orderItemEntity.setGamePrice(orderGameVo.getPrice());
            orderItemEntity.setGameQuantity(1);  //购买数量
            orderItemEntity.setCouponAmount(orderGameVo.getPrice().subtract(orderGameVo.getSeckillPrice() == null ? new BigDecimal(0) : orderGameVo.getSeckillPrice()));
            orderItemEntity.setRealAmount(orderGameVo.getSeckillPrice() == null ? orderGameVo.getPrice() : orderGameVo.getSeckillPrice());
            orderItemEntities.add(orderItemEntity);
        });
        return orderItemEntities;
    }

    /**
     * 校验价格信息
     * @param orderEntity
     * @param orderItemEntities
     */
    private void checkTotalPrice(OrderEntity orderEntity, List<OrderItemEntity> orderItemEntities) {
        BigDecimal totalPrice = new BigDecimal(0);
        for (OrderItemEntity orderItemEntity : orderItemEntities) {
            //每个商品的实际支付价格
            totalPrice = totalPrice.add(orderItemEntity.getRealAmount());
        }
        if (totalPrice.compareTo(orderEntity.getPayAmount()) != 0) {
            throw new OrderException("价格错误，请重试", BizCodeEnume.SUBMIT_ORDER_FAIL.getCode());
        }
    }

}